
function isMatch(s: string, p: string): boolean {
    var loop = (s: string, p: string, cache: any): boolean => {
        var tryValue = cache[s + '\t' + p];
        var addAndReturn = (value: boolean) => cache[s + '\t' + p] = value;
        if (tryValue === undefined) {
            const head1 = s.substring(0, 1);
            const head2 = p.substring(0, 1);
            const tail1 = s.substring(1);
            const tail2 = p.substring(1);

            if (tail2.substring(0, 1) == "*") {
                if (loop(s, p.substring(2), cache))
                    return addAndReturn(true);

                for(let i = -1; i < s.length && (i < 0 ||head2 == '.' || head2 == s[i]); i++) {
                    if (loop(s.substring(i+1), p.substring(2), cache)) {
                        return addAndReturn(true);
                    }
                }
                return addAndReturn(false);
            }
            else {
                let headsMatch = (head1 == head2) || (head1.length == 1 && head2 == '.');
                return addAndReturn(headsMatch && (tail1 == tail2 || loop(tail1, tail2, cache)));
            }
        }
            else tryValue
    };
    return loop(s, p, {});
}; 

console.log("start");

console.assert( isMatch("",""));
console.assert( isMatch("a","") == false);
console.assert( isMatch("","a") == false);
console.assert( isMatch("a","."));
console.assert( isMatch("",".") == false);

console.assert( isMatch("aa","a") == false);
console.assert( isMatch("aa","aa"));
console.assert( isMatch("aaa","aa") == false);
console.assert( isMatch("aa", "a*"));
console.assert( isMatch("aa", ".*"));
console.assert( isMatch("ab", ".*"));
console.assert( isMatch("aab", "c*a*b"));

console.assert( isMatch("ab", ".*c") == false);

//console.assert( isMatch("aba", "a*c*a") == false);
console.assert( isMatch("aaba", "ab*a*c*a") == false);

console.log("end");
